#include <map>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <errno.h>
#include <thread>

#include "tcpServer.h"
#include "netMessage.h"

using namespace std;

/**********************************************************************************
 * Imple 数据管理类                                                                *
 **********************************************************************************/
class TcpServer::Imple
{
public:
    Imple(string addr, int port);
    ~Imple();

public:
    string      m_address;          // 服务器绑定 IP d地址
    int         m_port;             // 服务器绑定端口号
    int         m_serverFd;         // 服务器 socket

    int         m_epollFd;          // epoll 描述符
    int         m_clientFd;         // 当前客户端网络描述符
    const int   m_epollMaxEvent;    // 最大事件个数
    struct epoll_event *m_events;   // 客户端事件列表

    bool        m_running;
    thread      *m_epollThread;
};

TcpServer::Imple::Imple(string addr, int port) 
            : m_address(addr)
            , m_port(port)
            , m_serverFd(0)
            , m_epollFd(0)
            , m_clientFd(0)
            , m_epollMaxEvent(1000)
            , m_events(nullptr)
            , m_running(true)
{
}

TcpServer::Imple::~Imple()
{
    if(m_events != nullptr) {
        delete []m_events;
        m_events = nullptr;
    }
    close(m_serverFd);
    close(m_epollFd);
}


void TcpServer::epollThread()
{
    while(m_imple->m_running) {
        int timeout = -1;
        int ret = epoll_wait(m_imple->m_epollFd, m_imple->m_events, m_imple->m_epollMaxEvent, timeout);
        if(ret == -1) {
            cout << "errno: " << errno << endl;
            perror("error: epoll_wait");
            if(m_imple->m_events != nullptr) {
                delete []m_imple->m_events;
                m_imple->m_events = nullptr;
            }
            close(m_imple->m_serverFd);
            close(m_imple->m_epollFd);
            break;
        }

        for(int i = 0; i < ret; i++) {
            // 新客户端连接
            if(m_imple->m_events[i].data.fd == m_imple->m_serverFd) {
                newClientHandle();
            }
            // 接收到数据，读取 socket 消息
            else if(m_imple->m_events[i].events & EPOLLIN) {
                CommProtocol t;
                memset(&t, 0, sizeof(t));
                int rlen = recv(m_imple->m_events[i].data.fd, &t, sizeof(t), 0);
                // 客户端断开连接
                if(rlen == 0) {
                    clientDisconnected(m_imple->m_events[i].data.fd);
                }
                // 接收数据错误
                else if(rlen < 0) {
                }
                // 接收数据
                else {
                    parseClientData(t);
                }
            }
            // 数据待发送
            else if(m_imple->m_events[i].events & EPOLLOUT) {
            }
            else {
            }
        }
    }
}



/**********************************************************************************
 * TcpServer 实现部分代码                                                          *
 **********************************************************************************/
TcpServer::TcpServer(string addr, int port) : m_imple(new Imple(addr, port))
{
    m_imple->m_serverFd = socket(AF_INET, SOCK_STREAM, 0);
    if(m_imple->m_serverFd <= 0) {
        perror("server socket");
        exit(-1);
    }

    struct sockaddr_in serverAddr;
    serverAddr.sin_family       = AF_INET;
    serverAddr.sin_port         = htons(m_imple->m_port);
    serverAddr.sin_addr.s_addr  = inet_addr(m_imple->m_address.c_str());

    // 绑定 socket，成功返回 0，失败返回 -1
    int ret = bind(m_imple->m_serverFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
    if(ret < 0) {
        perror("bsocket ind"); 
        exit(-1);
    }

    // 监听，成功返回 0， 失败返回 -1
    ret = listen(m_imple->m_serverFd, 20);
    if(ret < 0) {
        perror("socket listen");
        exit(-1);
    }

    // epoll初始化
    m_imple->m_epollFd = epoll_create(m_imple->m_epollMaxEvent);
    struct epoll_event event;
    event.data.fd = m_imple->m_serverFd;

    ret = epoll_ctl(m_imple->m_epollFd, EPOLL_CTL_ADD, m_imple->m_serverFd, &event);
    if(ret < 0) {
        perror("epoll_ctl");
        close(m_imple->m_serverFd);
        close(m_imple->m_epollFd);
        exit(-1);
    }
    m_imple->m_events = new struct epoll_event[m_imple->m_epollMaxEvent];
    m_imple->m_epollThread = new thread(&TcpServer::epollThread, this);
    m_imple->m_epollThread->detach();
    
}

TcpServer::~TcpServer()
{
    if(m_imple != nullptr) {
        delete m_imple;
        m_imple = nullptr;
    }
}

void TcpServer::newClientHandle()
{
    struct sockaddr_in clientAddr;
    socklen_t len = sizeof(struct sockaddr_in);
    bzero(&clientAddr, len);

    int clientFd = accept(m_imple->m_serverFd, (struct sockaddr*)&clientAddr, &len);
    if(clientFd < 0) {
        perror("error: bad accept");
        return;
    }
    
    struct epoll_event event;
    event.data.fd = clientFd;
    event.events = EPOLLIN | EPOLLET;
    epoll_ctl(m_imple->m_epollFd, EPOLL_CTL_ADD, clientFd, &event);

    NewClientMessage msg;
    msg.clientFd    = clientFd;
    msg.addr        = inet_ntoa(clientAddr.sin_addr);
    msg.port        = clientAddr.sin_port;
    msg.post();
}

void TcpServer::clientDisconnected(int cfd)
{
    struct epoll_event ev;
    ev.data.fd = cfd;
    ev.events = EPOLLIN | EPOLLET;
    epoll_ctl(m_imple->m_epollFd, EPOLL_CTL_ADD, cfd, &ev);
    cout << cfd << " exit.\n";
    epoll_ctl(m_imple->m_epollFd, EPOLL_CTL_DEL, cfd, &ev);
    close(cfd);

    ClientDataMessage msg;
    msg.type = CDT_CLOSE;
    msg.post();
}

void TcpServer::parseClientData(const CommProtocol& c)
{
    if(c.header == 0x17) {
        switch (c.type)
        {
        case DT_LOGIN:
        {
            ClientDataMessage msg;
            msg.type = CDT_LOGIN;
            msg.data = {{"id",      c.data.login_info.id}, 
                        {"user",    c.data.login_info.user}, 
                        {"passwd",  c.data.login_info.passwd}};
            msg.post();
            break;
        }
        case DT_REGISTER:                        
        {
            ClientDataMessage msg;
            msg.type = CDT_REGISTER;
            msg.data = {{"user",    c.data.login_info.user},
                        {"passwd",  c.data.login_info.passwd}};
            msg.post();
            break;
        }
        
        default:
            break;
        }
    }
    else {
    }
}

bool TcpServer::handle(const ServerCmdMessage *pMsg)
{
    if(pMsg->type == SC_EXIT) {
        m_imple->m_running = false;
        close(m_imple->m_serverFd);
        cout << "close server.\n";
    }
}